/****************************************************************************************/
/*   程序名：_freecplus.h，此程序是freecplus框架公用函数和类的定义文件。                */
/*   作者：C语言技术网(www.freecplus.net) 日期：20190525。                              */
/*                                                                                      */
/*   freecplus开源许可协议正文如下：                                                    */
/*     版权所有 (c) 2008-2020，码农有道，C语言技术网（www.freecplus.net）。             */
/*     特此向任何得到本软件副本或相关文档的人授权：被授权人有权使用、复制、修改、       */
/*   合并、出版、发布、散布、再授权和/或贩售软件及软件的副本，及授予被供应人同等        */
/*   权利，只需服从以下义务：                                                           */
/*     在软件和软件的所有副本中都必须包含以上版权声明和本许可声明。                     */
/*     如果违返，无论在本软件的使用上或其他买卖交易中是否涉及合同，都属于侵权行为，     */
/*   我们保留追究的权利。                                                               */
/*   freecplus开源许可协议正文结束。                                                    */
/****************************************************************************************/

#include <freecplus/_freecplus.h>

// 安全的strcpy函数。
// dest：目标字符串，不需要初始化，在STRCPY函数中有初始化代码。
// destlen：目标字符串dest占用内存的大小。
// src：原字符串。
// 返回值：目标字符串dest的地址。
char *STRCPY(char *dest, const size_t destlen, const char *src) {
    if (dest == 0)
        return 0;
    memset(dest, 0, destlen);  // 初始化dest。
    if (src == 0)
        return dest;

    if (strlen(src) > destlen - 1)
        strncpy(dest, src, destlen - 1);
    else
        strcpy(dest, src);

    return dest;
}

// 安全的strncpy函数。
// dest：目标字符串，不需要初始化，在STRCPY函数中有初始化代码。
// destlen：目标字符串dest占用内存的大小。
// src：原字符串。
// n：待复制的字节数。
// 返回值：目标字符串dest的地址。
char *STRNCPY(char *dest, const size_t destlen, const char *src, size_t n) {
    if (dest == 0)
        return 0;
    memset(dest, 0, destlen);  // 初始化dest。
    if (src == 0)
        return dest;

    if (n > destlen - 1)
        strncpy(dest, src, destlen - 1);
    else
        strncpy(dest, src, n);

    return dest;
}

// 安全的strcat函数。
// dest：目标字符串，注意，如果dest从未使过，那么需要至少一次初始化。
// destlen：目标字符串dest占用内存的大小。
// src：待追加字符串。
// 返回值：目标字符串dest的地址。
char *STRCAT(char *dest, const size_t destlen, const char *src) {
    if (dest == 0)
        return 0;
    if (src == 0)
        return dest;

    unsigned int left = destlen - 1 - strlen(dest);

    if (strlen(src) > left) {
        strncat(dest, src, left);
        dest[destlen - 1] = 0;
    }
    else
        strcat(dest, src);

    return dest;
}

// 安全的strncat函数。
// dest：目标字符串，注意，如果dest从未使过，那么需要至少一次初始化。
// destlen：目标字符串dest占用内存的大小。
// src：待追加字符串。
// n：待追加的字节数。
// 返回值：目标字符串dest的地址。
char *STRNCAT(char *dest, const size_t destlen, const char *src, size_t n) {
    if (dest == 0)
        return 0;
    if (src == 0)
        return dest;

    size_t left = destlen - 1 - strlen(dest);

    if (n > left) {
        strncat(dest, src, left);
        dest[destlen - 1] = 0;
    }
    else
        strncat(dest, src, n);

    return dest;
}

// 安全的sprintf函数。
// 将可变参数(...)按照fmt描述的格式输出到dest字符串中。
// dest：输出字符串，不需要初始化，在SPRINTF函数中会对它进行初始化。
// destlen：输出字符串dest占用内存的大小，如果格式化后的字符串内容的长度大于destlen-1，后面的内容将丢弃。
// fmt：格式控制描述。
// ...：填充到格式控制描述fmt中的参数。
// 返回值：格式化后的内容的长度，程序员一般不关心返回值。
int SPRINTF(char *dest, const size_t destlen, const char *fmt, ...) {
    if (dest == 0)
        return -1;

    memset(dest, 0, destlen);

    va_list arg;
    va_start(arg, fmt);
    vsnprintf(dest, destlen, fmt, arg);
    va_end(arg);

    return strlen(dest);
}

// 安全的snprintf函数。
// 将可变参数(...)按照fmt描述的格式输出到dest字符串中。
// dest：输出字符串，不需要初始化，在SNPRINTF函数中会对它进行初始化。
// destlen：输出字符串dest占用内存的大小，如果格式化后的字符串内容的长度大于destlen-1，后面的内容将丢弃。
// n：把格式化后的字符串截取n-1存放到dest中，如果n>destlen，则取destlen-1。
// fmt：格式控制描述。
// ...：填充到格式控制描述fmt中的参数。
// 返回值：格式化后的内容的长度，程序员一般不关心返回值。
int SNPRINTF(char *dest, const size_t destlen, size_t n, const char *fmt, ...) {
    if (dest == 0)
        return -1;

    memset(dest, 0, destlen);

    int len = n;
    if (n > destlen)
        len = destlen;

    va_list arg;
    va_start(arg, fmt);
    vsnprintf(dest, len, fmt, arg);
    va_end(arg);

    return strlen(dest);
}

// 删除字符串左边指定的字符。
// str：待处理的字符串。
// chr：需要删除的字符。
void DeleteLChar(char *str, const char chr) {
    if (str == 0)
        return;
    if (strlen(str) == 0)
        return;

    char strTemp[strlen(str) + 1];

    int iTemp = 0;

    memset(strTemp, 0, sizeof(strTemp));
    strcpy(strTemp, str);

    while (strTemp[iTemp] == chr)
        iTemp++;

    memset(str, 0, strlen(str) + 1);

    strcpy(str, strTemp + iTemp);

    return;
}

// 删除字符串右边指定的字符。
// str：待处理的字符串。
// chr：需要删除的字符。
void DeleteRChar(char *str, const char chr) {
    if (str == 0)
        return;
    if (strlen(str) == 0)
        return;

    int istrlen = strlen(str);

    while (istrlen > 0) {
        if (str[istrlen - 1] != chr)
            break;

        str[istrlen - 1] = 0;

        istrlen--;
    }
}

// 删除字符串左右两边指定的字符。
// str：待处理的字符串。
// chr：需要删除的字符。
void DeleteLRChar(char *str, const char chr) {
    DeleteLChar(str, chr);
    DeleteRChar(str, chr);
}

// 把字符串中的小写字母转换成大写，忽略不是字母的字符。
// str：待转换的字符串，支持char[]和std::string两种类型。
void ToUpper(char *str) {
    if (str == 0)
        return;

    if (strlen(str) == 0)
        return;

    int istrlen = strlen(str);

    for (int ii = 0; ii < istrlen; ii++) {
        if ((str[ii] >= 'a') && (str[ii] <= 'z'))
            str[ii] = str[ii] - 32;
    }
}

void ToUpper(std::string &str) {
    if (str.empty())
        return;

    char strtemp[str.size() + 1];

    memset(strtemp, 0, sizeof(strtemp));
    strcpy(strtemp, str.c_str());

    ToUpper(strtemp);

    str = strtemp;

    return;
}

// 把字符串中的大写字母转换成小写，忽略不是字母的字符。
// str：待转换的字符串，支持char[]和std::string两种类型。
void ToLower(char *str) {
    if (str == 0)
        return;

    if (strlen(str) == 0)
        return;

    int istrlen = strlen(str);

    for (int ii = 0; ii < istrlen; ii++) {
        if ((str[ii] >= 'A') && (str[ii] <= 'Z'))
            str[ii] = str[ii] + 32;
    }
}

void ToLower(std::string &str) {
    if (str.empty())
        return;

    char strtemp[str.size() + 1];

    memset(strtemp, 0, sizeof(strtemp));
    strcpy(strtemp, str.c_str());

    ToLower(strtemp);

    str = strtemp;

    return;
}

// 字符串替换函数
// 在字符串str中，如果存在字符串str1，就替换为字符串str2。
// str：待处理的字符串。
// str1：旧的内容。
// str2：新的内容。
// bloop：是否循环执行替换。
// 注意：
// 1、如果str2比str1要长，替换后str会变长，所以必须保证str有足够的长度，否则内存会溢出。
// 2、如果str2中包函了str1的内容，且bloop为true，存在逻辑错误，将不执行任何替换。
void UpdateStr(char *str, const char *str1, const char *str2, bool bloop) {
    if (str == 0)
        return;
    if (strlen(str) == 0)
        return;
    if ((str1 == 0) || (str2 == 0))
        return;

    // 如果bloop为true并且str2中包函了str1的内容，直接返回，因为会进入死循环，最终导致内存溢出。
    if ((bloop == true) && (strstr(str2, str1) > 0))
        return;

    // 尽可能分配更多的空间，但仍有可能出现内存溢出的情况，最好优化成std::string。
    int ilen = strlen(str) * 10;
    if (ilen < 1000)
        ilen = 1000;

    char strTemp[ilen];

    char *strStart = str;

    char *strPos = 0;

    while (true) {
        if (bloop == true) {
            strPos = strstr(str, str1);
        }
        else {
            strPos = strstr(strStart, str1);
        }

        if (strPos == 0)
            break;

        memset(strTemp, 0, sizeof(strTemp));
        strncpy(strTemp, str, strPos - str);
        strcat(strTemp, str2);
        strcat(strTemp, strPos + strlen(str1));
        strcpy(str, strTemp);

        strStart = strPos + strlen(str2);
    }
}

// 从一个字符串中提取出数字的内容，存放到另一个字符串中。
// src：源字符串。
// dest：目标字符串。
// bsigned：是否包括符号（+和-），true-包括；false-不包括。
// bdot：是否包括小数点的圆点符号，true-包括；false-不包括。
void PickNumber(const char *src, char *dest, const bool bsigned, const bool bdot) {
    if (dest == 0)
        return;
    if (src == 0) {
        strcpy(dest, "");
        return;
    }

    char strtemp[strlen(src) + 1];
    memset(strtemp, 0, sizeof(strtemp));
    strcpy(strtemp, src);
    DeleteLRChar(strtemp, ' ');

    int ipossrc, iposdst, ilen;
    ipossrc = iposdst = ilen = 0;

    ilen = strlen(strtemp);

    for (ipossrc = 0; ipossrc < ilen; ipossrc++) {
        if ((bsigned == true) && (strtemp[ipossrc] == '+')) {
            dest[iposdst++] = strtemp[ipossrc];
            continue;
        }

        if ((bsigned == true) && (strtemp[ipossrc] == '-')) {
            dest[iposdst++] = strtemp[ipossrc];
            continue;
        }

        if ((bdot == true) && (strtemp[ipossrc] == '.')) {
            dest[iposdst++] = strtemp[ipossrc];
            continue;
        }

        if (isdigit(strtemp[ipossrc]))
            dest[iposdst++] = strtemp[ipossrc];
    }

    dest[iposdst] = 0;
}

// 正则表达式，判断一个字符串是否匹配另一个字符串。
// str：需要判断的字符串，精确表示的字符串，如文件名"freecplus.cpp"。
// rules：匹配规则表达式，用星号"*"表示任意字符串，多个字符串之间用半角的逗号分隔，如"*.h,*.cpp"。
// 注意，str参数不支持"*"，rules参数支持"*"，函数在判断str是否匹配rules的时候，会忽略字母的大小写。
bool MatchStr(const std::string str, const std::string rules) {
    // 如果用于比较的字符是空的，返回false
    if (rules.size() == 0)
        return false;

    // 如果被比较的字符串是"*"，返回true
    if (rules == "*")
        return true;

    // 处理文件名匹配规则中的时间匹配dd-nn.mm
    char strTemp[2049];
    memset(strTemp, 0, sizeof(strTemp));
    strncpy(strTemp, rules.c_str(), 2000);

    int     ii, jj;
    int     iPOS1, iPOS2;
    CCmdStr CmdStr, CmdSubStr;

    std::string strFileName, strMatchStr;

    strFileName = str;
    strMatchStr = strTemp;

    // 把字符串都转换成大写后再来比较
    ToUpper(strFileName);
    ToUpper(strMatchStr);

    CmdStr.SplitToCmd(strMatchStr, ",");

    for (ii = 0; ii < CmdStr.CmdCount(); ii++) {
        // 如果为空，就一定要跳过，否则就会被配上
        if (CmdStr.m_vCmdStr[ii].empty() == true)
            continue;

        iPOS1 = iPOS2 = 0;
        CmdSubStr.SplitToCmd(CmdStr.m_vCmdStr[ii], "*");

        for (jj = 0; jj < CmdSubStr.CmdCount(); jj++) {
            // 如果是文件名的首部
            if (jj == 0) {
                if (strncmp(strFileName.c_str(), CmdSubStr.m_vCmdStr[jj].c_str(), CmdSubStr.m_vCmdStr[jj].size()) != 0)
                    break;
            }

            // 如果是文件名的尾部
            if (jj == CmdSubStr.CmdCount() - 1) {
                if (strcmp(strFileName.c_str() + strFileName.size() - CmdSubStr.m_vCmdStr[jj].size(), CmdSubStr.m_vCmdStr[jj].c_str()) != 0)
                    break;
            }

            iPOS2 = strFileName.find(CmdSubStr.m_vCmdStr[jj], iPOS1);

            if (iPOS2 < 0)
                break;

            iPOS1 = iPOS2 + CmdSubStr.m_vCmdStr[jj].size();
        }

        if (jj == CmdSubStr.CmdCount())
            return true;
    }

    return false;
}

// 正则表达式，判断一个字符串是否匹配另一个字符串。
// 保留MatchFileName函数是为了兼容之前的版本。
bool MatchFileName(const std::string in_FileName, const std::string in_MatchStr) {
    return MatchStr(in_FileName, in_MatchStr);
}

// 统计字符串的字数，全角的汉字和全角的标点符号算一个字，半角的汉字和半角的标点符号也算一个字。
// str：待统计的字符串。
// 返回值：字符串str的字数。
int Words(const char *str) {
    int  wlen    = 0;
    bool biswide = 0;
    int  ilen    = strlen(str);

    for (int ii = 0; ii < ilen; ii++) {
        if ((unsigned int)str[ii] < 128) {
            wlen = wlen + 1;
        }
        else {
            if (biswide == true) {
                wlen    = wlen + 1;
                biswide = false;
            }
            else {
                biswide = true;
            }
        }
    }

    return wlen;
}

CFile::CFile()  // 类的构造函数
{
    m_fp        = 0;
    m_bEnBuffer = true;
    memset(m_filename, 0, sizeof(m_filename));
    memset(m_filenametmp, 0, sizeof(m_filenametmp));
}

// 关闭文件指针
void CFile::Close() {
    if (m_fp == 0)
        return;

    fclose(m_fp);  // 关闭文件指针

    m_fp = 0;
    memset(m_filename, 0, sizeof(m_filename));

    // 如果存在临时文件，就删除它。
    if (strlen(m_filenametmp) != 0)
        remove(m_filenametmp);

    memset(m_filenametmp, 0, sizeof(m_filenametmp));
}

// 判断文件是否已打开
bool CFile::IsOpened() {
    if (m_fp == 0)
        return false;

    return true;
}

// 关闭文件指针，并删除文件
bool CFile::CloseAndRemove() {
    if (m_fp == 0)
        return true;

    fclose(m_fp);  // 关闭文件指针

    m_fp = 0;

    if (remove(m_filename) != 0) {
        memset(m_filename, 0, sizeof(m_filename));
        return false;
    }

    memset(m_filename, 0, sizeof(m_filename));
    memset(m_filenametmp, 0, sizeof(m_filenametmp));

    return true;
}

CFile::~CFile()  // 类的析构函数
{
    Close();
}

// 打开文件，参数与FOPEN相同，打开成功true，失败返回false
bool CFile::Open(const char *filename, const char *openmode, bool bEnBuffer) {
    Close();

    if ((m_fp = FOPEN(filename, openmode)) == 0)
        return false;

    memset(m_filename, 0, sizeof(m_filename));

    strncpy(m_filename, filename, 300);

    m_bEnBuffer = bEnBuffer;

    return true;
}

// 专为改名而打开文件，参数与fopen相同，打开成功true，失败返回false
bool CFile::OpenForRename(const char *filename, const char *openmode, bool bEnBuffer) {
    Close();

    memset(m_filename, 0, sizeof(m_filename));
    strncpy(m_filename, filename, 300);

    memset(m_filenametmp, 0, sizeof(m_filenametmp));
    SNPRINTF(m_filenametmp, sizeof(m_filenametmp), 300, "%s.tmp", m_filename);

    if ((m_fp = FOPEN(m_filenametmp, openmode)) == 0)
        return false;

    m_bEnBuffer = bEnBuffer;

    return true;
}

// 关闭文件并改名
bool CFile::CloseAndRename() {
    if (m_fp == 0)
        return false;

    fclose(m_fp);  // 关闭文件指针

    m_fp = 0;

    if (rename(m_filenametmp, m_filename) != 0) {
        remove(m_filenametmp);
        memset(m_filename, 0, sizeof(m_filename));
        memset(m_filenametmp, 0, sizeof(m_filenametmp));
        return false;
    }

    memset(m_filename, 0, sizeof(m_filename));
    memset(m_filenametmp, 0, sizeof(m_filenametmp));

    return true;
}

// 调用fprintf向文件写入数据
void CFile::Fprintf(const char *fmt, ...) {
    if (m_fp == 0)
        return;

    va_list arg;
    va_start(arg, fmt);
    vfprintf(m_fp, fmt, arg);
    va_end(arg);

    if (m_bEnBuffer == false)
        fflush(m_fp);
}

// 调用fgets从文件中读取一行，bDelCRT=true删除换行符，false不删除，缺省为false
bool CFile::Fgets(char *buffer, const int readsize, bool bdelcrt) {
    if (m_fp == 0)
        return false;

    memset(buffer, 0, readsize + 1);  // 调用者必须保证buffer的空间足够，否则这里会内存溢出。

    if (fgets(buffer, readsize, m_fp) == 0)
        return false;

    if (bdelcrt == true) {
        DeleteRChar(buffer, '\n');
        DeleteRChar(buffer, '\r');  // 如果文件是windows格式，还要删除'\r'。
    }

    return true;
}

// 从文件文件中读取一行
// strEndStr是一行数据的结束标志，如果为空，则以换行符"\n"为结束标志。
bool CFile::FFGETS(char *buffer, const int readsize, const char *endbz) {
    if (m_fp == 0)
        return false;

    return FGETS(m_fp, buffer, readsize, endbz);
}

// 调用fread从文件中读取数据。
size_t CFile::Fread(void *ptr, size_t size) {
    if (m_fp == 0)
        return -1;

    return fread(ptr, 1, size, m_fp);
}

// 调用fwrite向文件中写数据
size_t CFile::Fwrite(const void *ptr, size_t size) {
    if (m_fp == 0)
        return -1;

    size_t tt = fwrite(ptr, 1, size, m_fp);

    if (m_bEnBuffer == false)
        fflush(m_fp);

    return tt;
}

// 从文本文件中读取一行。
// fp：已打开的文件指针。
// buffer：用于存放读取的内容。
// readsize：本次打算读取的字节数，如果已经读取到了结束标志，函数返回。
// endbz：行内容结束的标志，缺省为空，表示行内容以"\n"为结束标志。
// 返回值：true-成功；false-失败，一般情况下，失败可以认为是文件已结束。
bool FGETS(const FILE *fp, char *buffer, const int readsize, const char *endbz) {
    if (fp == 0)
        return false;

    memset(buffer, 0, readsize + 1);  // 调用者必须保证buffer的空间足够，否则这里会内存溢出。

    char strline[readsize + 1];

    while (true) {
        memset(strline, 0, sizeof(strline));

        if (fgets(strline, readsize, (FILE *)fp) == 0)
            break;

        // 防止buffer溢出
        if ((strlen(buffer) + strlen(strline)) >= (unsigned int)readsize)
            break;

        strcat(buffer, strline);

        if (endbz == 0)
            return true;
        else if (strstr(strline, endbz) != 0)
            return true;
    }

    return false;
}

CCmdStr::CCmdStr() {
    m_vCmdStr.clear();
}

// 把字符串拆分到m_vCmdStr容器中。
// buffer：待拆分的字符串。
// sepstr：buffer字符串中字段内容的分隔符，注意，分隔符是字符串，如","、" "、"|"、"~!~"。
// bdelspace：是否删除拆分后的字段内容前后的空格，true-删除；false-不删除，缺省删除。
void CCmdStr::SplitToCmd(const std::string buffer, const char *sepstr, const bool bdelspace) {
    // 清除所有的旧数据
    m_vCmdStr.clear();

    int         iPOS = 0;
    std::string srcstr, substr;

    srcstr = buffer;

    char str[2048];

    while ((iPOS = srcstr.find(sepstr)) >= 0) {
        substr = srcstr.substr(0, iPOS);

        if (bdelspace == true) {
            memset(str, 0, sizeof(str));

            strncpy(str, substr.c_str(), 2000);

            DeleteLRChar(str, ' ');

            substr = str;
        }

        m_vCmdStr.push_back(substr);

        iPOS = iPOS + strlen(sepstr);

        srcstr = srcstr.substr(iPOS, srcstr.size() - iPOS);
    }

    substr = srcstr;

    if (bdelspace == true) {
        memset(str, 0, sizeof(str));

        strncpy(str, substr.c_str(), 2000);

        DeleteLRChar(str, ' ');

        substr = str;
    }

    m_vCmdStr.push_back(substr);

    return;
}

int CCmdStr::CmdCount() {
    return m_vCmdStr.size();
}

bool CCmdStr::GetValue(const int inum, char *value, const int ilen) {
    if ((inum >= (int)m_vCmdStr.size()) || (value == 0))
        return false;

    if (ilen > 0)
        memset(value, 0, ilen + 1);  // 调用者必须保证value的空间足够，否则这里会内存溢出。

    if ((m_vCmdStr[inum].length() <= (unsigned int)ilen) || (ilen == 0)) {
        strcpy(value, m_vCmdStr[inum].c_str());
    }
    else {
        strncpy(value, m_vCmdStr[inum].c_str(), ilen);
        value[ilen] = 0;
    }

    return true;
}

bool CCmdStr::GetValue(const int inum, int *value) {
    if ((inum >= (int)m_vCmdStr.size()) || (value == 0))
        return false;

    (*value) = 0;

    if (inum >= (int)m_vCmdStr.size())
        return false;

    (*value) = atoi(m_vCmdStr[inum].c_str());

    return true;
}

bool CCmdStr::GetValue(const int inum, unsigned int *value) {
    if ((inum >= (int)m_vCmdStr.size()) || (value == 0))
        return false;

    (*value) = 0;

    if (inum >= (int)m_vCmdStr.size())
        return false;

    (*value) = atoi(m_vCmdStr[inum].c_str());

    return true;
}

bool CCmdStr::GetValue(const int inum, long *value) {
    if ((inum >= (int)m_vCmdStr.size()) || (value == 0))
        return false;

    (*value) = 0;

    if (inum >= (int)m_vCmdStr.size())
        return false;

    (*value) = atol(m_vCmdStr[inum].c_str());

    return true;
}

bool CCmdStr::GetValue(const int inum, unsigned long *value) {
    if ((inum >= (int)m_vCmdStr.size()) || (value == 0))
        return false;

    (*value) = 0;

    if (inum >= (int)m_vCmdStr.size())
        return false;

    (*value) = atol(m_vCmdStr[inum].c_str());

    return true;
}

bool CCmdStr::GetValue(const int inum, double *value) {
    if ((inum >= (int)m_vCmdStr.size()) || (value == 0))
        return false;

    (*value) = 0;

    if (inum >= (int)m_vCmdStr.size())
        return false;

    (*value) = (double)atof(m_vCmdStr[inum].c_str());

    return true;
}

bool CCmdStr::GetValue(const int inum, bool *value) {
    if ((inum >= (int)m_vCmdStr.size()) || (value == 0))
        return false;

    (*value) = 0;

    if (inum >= (int)m_vCmdStr.size())
        return false;

    char strTemp[11];
    memset(strTemp, 0, sizeof(strTemp));
    strncpy(strTemp, m_vCmdStr[inum].c_str(), 10);

    ToUpper(strTemp);  // 转换为大写来判断。
    if (strcmp(strTemp, "TRUE") == 0)
        (*value) = true;

    return true;
}

CCmdStr::~CCmdStr() {
    m_vCmdStr.clear();
}

bool GetXMLBuffer(const char *xmlbuffer, const char *fieldname, char *value, const int ilen) {
    if (value == 0)
        return false;

    if (ilen > 0)
        memset(value, 0, ilen + 1);  // 调用者必须保证value的空间足够，否则这里会内存溢出。

    char *start = 0, *end = 0;
    char  m_SFieldName[51], m_EFieldName[51];

    int m_NameLen = strlen(fieldname);
    memset(m_SFieldName, 0, sizeof(m_SFieldName));
    memset(m_EFieldName, 0, sizeof(m_EFieldName));

    snprintf(m_SFieldName, 50, "<%s>", fieldname);
    snprintf(m_EFieldName, 50, "</%s>", fieldname);

    start = 0;
    end   = 0;

    start = (char *)strstr(xmlbuffer, m_SFieldName);

    if (start != 0) {
        end = (char *)strstr(start, m_EFieldName);
    }

    if ((start == 0) || (end == 0)) {
        return false;
    }

    int m_ValueLen = end - start - m_NameLen - 2 + 1;

    if (((m_ValueLen - 1) <= ilen) || (ilen == 0)) {
        strncpy(value, start + m_NameLen + 2, m_ValueLen - 1);
        value[m_ValueLen - 1] = 0;
    }
    else {
        strncpy(value, start + m_NameLen + 2, ilen);
        value[ilen] = 0;
    }

    DeleteLRChar(value, ' ');

    return true;
}

bool GetXMLBuffer(const char *xmlbuffer, const char *fieldname, bool *value) {
    if (value == 0)
        return false;

    (*value) = false;

    char strTemp[51];

    memset(strTemp, 0, sizeof(strTemp));

    if (GetXMLBuffer(xmlbuffer, fieldname, strTemp, 10) == true) {
        ToUpper(strTemp);  // 转换为大写来判断。
        if (strcmp(strTemp, "TRUE") == 0) {
            (*value) = true;
            return true;
        }
    }

    return false;
}

bool GetXMLBuffer(const char *xmlbuffer, const char *fieldname, int *value) {
    if (value == 0)
        return false;

    (*value) = 0;

    char strTemp[51];

    memset(strTemp, 0, sizeof(strTemp));

    if (GetXMLBuffer(xmlbuffer, fieldname, strTemp, 50) == true) {
        (*value) = atoi(strTemp);
        return true;
    }

    return false;
}

bool GetXMLBuffer(const char *xmlbuffer, const char *fieldname, unsigned int *value) {
    if (value == 0)
        return false;

    (*value) = 0;

    char strTemp[51];

    memset(strTemp, 0, sizeof(strTemp));

    if (GetXMLBuffer(xmlbuffer, fieldname, strTemp, 50) == true) {
        (*value) = (unsigned int)atoi(strTemp);
        return true;
    }

    return false;
}

bool GetXMLBuffer(const char *xmlbuffer, const char *fieldname, long *value) {
    if (value == 0)
        return false;

    (*value) = 0;

    char strTemp[51];

    memset(strTemp, 0, sizeof(strTemp));

    if (GetXMLBuffer(xmlbuffer, fieldname, strTemp, 50) == true) {
        (*value) = atol(strTemp);
        return true;
    }

    return false;
}

bool GetXMLBuffer(const char *xmlbuffer, const char *fieldname, unsigned long *value) {
    if (value == 0)
        return false;

    (*value) = 0;

    char strTemp[51];

    memset(strTemp, 0, sizeof(strTemp));

    if (GetXMLBuffer(xmlbuffer, fieldname, strTemp, 50) == true) {
        (*value) = (unsigned long)atol(strTemp);
        return true;
    }

    return false;
}

bool GetXMLBuffer(const char *xmlbuffer, const char *fieldname, double *value) {
    if (value == 0)
        return false;

    (*value) = 0;

    char strTemp[51];

    memset(strTemp, 0, sizeof(strTemp));

    if (GetXMLBuffer(xmlbuffer, fieldname, strTemp, 50) == true) {
        (*value) = atof(strTemp);
        return true;
    }

    return false;
}

// 把整数表示的时间转换为字符串表示的时间。
// ltime：整数表示的时间。
// stime：字符串表示的时间。
// fmt：输出字符串时间stime的格式，与LocalTime函数的fmt参数相同，如果fmt的格式不正确，stime将为空。
void timetostr(const time_t ltime, char *stime, const char *fmt) {
    if (stime == 0)
        return;

    strcpy(stime, "");

    struct tm sttm = *localtime(&ltime);

    sttm.tm_year = sttm.tm_year + 1900;
    sttm.tm_mon++;

    if (fmt == 0) {
        snprintf(stime, 20, "%04u-%02u-%02u %02u:%02u:%02u", sttm.tm_year,
                 sttm.tm_mon, sttm.tm_mday, sttm.tm_hour,
                 sttm.tm_min, sttm.tm_sec);
        return;
    }

    if (strcmp(fmt, "yyyy-mm-dd hh24:mi:ss") == 0) {
        snprintf(stime, 20, "%04u-%02u-%02u %02u:%02u:%02u", sttm.tm_year,
                 sttm.tm_mon, sttm.tm_mday, sttm.tm_hour,
                 sttm.tm_min, sttm.tm_sec);
        return;
    }

    if (strcmp(fmt, "yyyy-mm-dd hh24:mi") == 0) {
        snprintf(stime, 17, "%04u-%02u-%02u %02u:%02u", sttm.tm_year,
                 sttm.tm_mon, sttm.tm_mday, sttm.tm_hour,
                 sttm.tm_min);
        return;
    }

    if (strcmp(fmt, "yyyy-mm-dd hh24") == 0) {
        snprintf(stime, 14, "%04u-%02u-%02u %02u", sttm.tm_year,
                 sttm.tm_mon, sttm.tm_mday, sttm.tm_hour);
        return;
    }

    if (strcmp(fmt, "yyyy-mm-dd") == 0) {
        snprintf(stime, 11, "%04u-%02u-%02u", sttm.tm_year, sttm.tm_mon, sttm.tm_mday);
        return;
    }

    if (strcmp(fmt, "yyyy-mm") == 0) {
        snprintf(stime, 8, "%04u-%02u", sttm.tm_year, sttm.tm_mon);
        return;
    }

    if (strcmp(fmt, "yyyymmddhh24miss") == 0) {
        snprintf(stime, 15, "%04u%02u%02u%02u%02u%02u", sttm.tm_year,
                 sttm.tm_mon, sttm.tm_mday, sttm.tm_hour,
                 sttm.tm_min, sttm.tm_sec);
        return;
    }

    if (strcmp(fmt, "yyyymmddhh24mi") == 0) {
        snprintf(stime, 13, "%04u%02u%02u%02u%02u", sttm.tm_year,
                 sttm.tm_mon, sttm.tm_mday, sttm.tm_hour,
                 sttm.tm_min);
        return;
    }

    if (strcmp(fmt, "yyyymmddhh24") == 0) {
        snprintf(stime, 11, "%04u%02u%02u%02u", sttm.tm_year,
                 sttm.tm_mon, sttm.tm_mday, sttm.tm_hour);
        return;
    }

    if (strcmp(fmt, "yyyymmdd") == 0) {
        snprintf(stime, 9, "%04u%02u%02u", sttm.tm_year, sttm.tm_mon, sttm.tm_mday);
        return;
    }

    if (strcmp(fmt, "hh24miss") == 0) {
        snprintf(stime, 7, "%02u%02u%02u", sttm.tm_hour, sttm.tm_min, sttm.tm_sec);
        return;
    }

    if (strcmp(fmt, "hh24mi") == 0) {
        snprintf(stime, 5, "%02u%02u", sttm.tm_hour, sttm.tm_min);
        return;
    }

    if (strcmp(fmt, "hh24") == 0) {
        snprintf(stime, 3, "%02u", sttm.tm_hour);
        return;
    }

    if (strcmp(fmt, "mi") == 0) {
        snprintf(stime, 3, "%02u", sttm.tm_min);
        return;
    }
}

/*
  取操作系统的时间，并把整数表示的时间转换为字符串表示的格式。
  stime：用于存放获取到的时间字符串。
  timetvl：时间的偏移量，单位：秒，0是缺省值，表示当前时间，30表示当前时间30秒之后的时间点，-30表示当前时间30秒之前的时间点。
  fmt：输出时间的格式，缺省是"yyyy-mm-dd hh24:mi:ss"，目前支持以下格式：
  "yyyy-mm-dd hh24:mi:ss"，此格式是缺省格式。
  "yyyymmddhh24miss"
  "yyyy-mm-dd"
  "yyyymmdd"
  "hh24:mi:ss"
  "hh24miss"
  "hh24:mi"
  "hh24mi"
  "hh24"
  "mi"
  注意：
    1）小时的表示方法是hh24，不是hh，这么做的目的是为了保持与数据库的时间表示方法一致；
    2）以上列出了常用的时间格式，如果不能满足你应用开发的需求，请修改源代码增加更多的格式支持；
    3）调用函数的时候，如果fmt与上述格式都匹配，stime的内容将为空。
*/
void LocalTime(char *stime, const char *fmt, const int timetvl) {
    if (stime == 0)
        return;

    time_t timer;

    time(&timer);
    timer = timer + timetvl;

    timetostr(timer, stime, fmt);
}

CLogFile::CLogFile(const long MaxLogSize) {
    m_tracefp = 0;
    memset(m_filename, 0, sizeof(m_filename));
    memset(m_openmode, 0, sizeof(m_openmode));
    m_bBackup    = true;
    m_bEnBuffer  = false;
    m_MaxLogSize = MaxLogSize;
    if (m_MaxLogSize < 10)
        m_MaxLogSize = 10;
}

CLogFile::~CLogFile() {
    Close();
}

void CLogFile::Close() {
    if (m_tracefp != 0) {
        fclose(m_tracefp);
        m_tracefp = 0;
    }

    memset(m_filename, 0, sizeof(m_filename));
    memset(m_openmode, 0, sizeof(m_openmode));
    m_bBackup   = true;
    m_bEnBuffer = false;
}

// 打开日志文件。
// filename：日志文件名，建议采用绝对路径，如果文件名中的目录不存在，就先创建目录。
// openmode：日志文件的打开方式，与fopen库函数打开文件的方式相同，缺省值是"a+"。
// bBackup：是否自动切换，true-切换，false-不切换，在多进程的服务程序中，如果多个进行共用一个日志文件，bBackup必须为false。
// bEnBuffer：是否启用文件缓冲机制，true-启用，false-不启用，如果启用缓冲区，那么写进日志文件中的内容不会立即写入文件，缺省是不启用。
bool CLogFile::Open(const char *filename, const char *openmode, bool bBackup, bool bEnBuffer) {
    // 如果文件指针是打开的状态，先关闭它。
    Close();

    strcpy(m_filename, filename);
    m_bEnBuffer = bEnBuffer;
    m_bBackup   = bBackup;
    if (openmode == 0)
        strcpy(m_openmode, "a+");
    else
        strcpy(m_openmode, openmode);

    if ((m_tracefp = FOPEN(m_filename, m_openmode)) == 0)
        return false;

    return true;
}

// 如果日志文件大于100M，就把当前的日志文件备份成历史日志文件，切换成功后清空当前日志文件的内容。
// 备份后的文件会在日志文件名后加上日期时间。
// 注意，在多进程的程序中，日志文件不可切换，多线的程序中，日志文件可以切换。
bool CLogFile::BackupLogFile() {
    if (m_tracefp == 0)
        return false;

    // 不备份
    if (m_bBackup == false)
        return true;

    fseek(m_tracefp, 0L, 2);

    if (ftell(m_tracefp) > m_MaxLogSize * 1024 * 1024) {
        fclose(m_tracefp);
        m_tracefp = 0;

        char strLocalTime[21];
        memset(strLocalTime, 0, sizeof(strLocalTime));
        LocalTime(strLocalTime, "yyyymmddhh24miss");

        char bak_filename[301];
        memset(bak_filename, 0, sizeof(bak_filename));
        snprintf(bak_filename, 300, "%s.%s", m_filename, strLocalTime);
        rename(m_filename, bak_filename);

        if ((m_tracefp = FOPEN(m_filename, m_openmode)) == 0)
            return false;
    }

    return true;
}

// 把内容写入日志文件，fmt是可变参数，使用方法与printf库函数相同。
// Write方法会写入当前的时间，WriteEx方法不写时间。
bool CLogFile::Write(const char *fmt, ...) {
    if (m_tracefp == 0)
        return false;

    if (BackupLogFile() == false)
        return false;

    char strtime[20];
    LocalTime(strtime);

    va_list ap;
    va_start(ap, fmt);
    fprintf(m_tracefp, "%s ", strtime);
    vfprintf(m_tracefp, fmt, ap);
    va_end(ap);

    if (m_bEnBuffer == false)
        fflush(m_tracefp);

    return true;
}

// 把内容写入日志文件，fmt是可变参数，使用方法与printf库函数相同。
// Write方法会写入当前的时间，WriteEx方法不写时间。
bool CLogFile::WriteEx(const char *fmt, ...) {
    if (m_tracefp == 0)
        return false;

    va_list ap;
    va_start(ap, fmt);
    vfprintf(m_tracefp, fmt, ap);
    va_end(ap);

    if (m_bEnBuffer == false)
        fflush(m_tracefp);

    return true;
}

CIniFile::CIniFile() {
}

bool CIniFile::LoadFile(const char *filename) {
    m_xmlbuffer.clear();

    CFile File;

    if (File.Open(filename, "r") == false)
        return false;

    char strLine[501];

    while (true) {
        memset(strLine, 0, sizeof(strLine));

        if (File.FFGETS(strLine, 500) == false)
            break;

        m_xmlbuffer = m_xmlbuffer + strLine;
    }

    if (m_xmlbuffer.length() < 10)
        return false;

    return true;
}

bool CIniFile::GetValue(const char *fieldname, bool *value) {
    return GetXMLBuffer(m_xmlbuffer.c_str(), fieldname, value);
}

bool CIniFile::GetValue(const char *fieldname, char *value, int ilen) {
    return GetXMLBuffer(m_xmlbuffer.c_str(), fieldname, value, ilen);
}

bool CIniFile::GetValue(const char *fieldname, int *value) {
    return GetXMLBuffer(m_xmlbuffer.c_str(), fieldname, value);
}

bool CIniFile::GetValue(const char *fieldname, unsigned int *value) {
    return GetXMLBuffer(m_xmlbuffer.c_str(), fieldname, value);
}

bool CIniFile::GetValue(const char *fieldname, long *value) {
    return GetXMLBuffer(m_xmlbuffer.c_str(), fieldname, value);
}

bool CIniFile::GetValue(const char *fieldname, unsigned long *value) {
    return GetXMLBuffer(m_xmlbuffer.c_str(), fieldname, value);
}

bool CIniFile::GetValue(const char *fieldname, double *value) {
    return GetXMLBuffer(m_xmlbuffer.c_str(), fieldname, value);
}

// 关闭全部的信号和输入输出
void CloseIOAndSignal() {
    int ii = 0;

    for (ii = 0; ii < 100; ii++) {
        signal(ii, SIG_IGN);
        close(ii);
    }
}

// 根据绝对路径的文件名或目录名逐级的创建目录。
// pathorfilename：绝对路径的文件名或目录名。
// bisfilename：说明pathorfilename的类型，true-pathorfilename是文件名，否则是目录名，缺省值为true。
// 返回值：true-创建成功，false-创建失败，如果返回失败，原因有大概有三种情况：1）权限不足； 2）pathorfilename参数不是合法的文件名或目录名；3）磁盘空间不足。
bool MKDIR(const char *filename, bool bisfilename) {
    // 检查目录是否存在，如果不存在，逐级创建子目录
    char strPathName[301];

    int ilen = strlen(filename);

    for (int ii = 1; ii < ilen; ii++) {
        if (filename[ii] != '/')
            continue;

        memset(strPathName, 0, sizeof(strPathName));
        strncpy(strPathName, filename, ii);

        if (access(strPathName, F_OK) == 0)
            continue;

        if (mkdir(strPathName, 0755) != 0)
            return false;
    }

    if (bisfilename == false) {
        if (access(filename, F_OK) != 0) {
            if (mkdir(filename, 0755) != 0)
                return false;
        }
    }

    return true;
}

// 打开文件。
// FOPEN函数调用fopen库函数打开文件，如果文件名中包含的目录不存在，就创建目录。
// FOPEN函数的参数和返回值与fopen函数完全相同。
// 在应用开发中，用FOPEN函数代替fopen库函数。
FILE *FOPEN(const char *filename, const char *mode) {
    if (MKDIR(filename) == false)
        return 0;

    return fopen(filename, mode);
}

// 获取文件的大小。
// filename：待获取的文件名，建议采用绝对路径的文件名。
// 返回值：如果文件不存在或没有访问权限，返回-1，成功返回文件的大小，单位是字节。
int FileSize(const char *filename) {
    struct stat st_filestat;

    if (stat(filename, &st_filestat) < 0)
        return -1;

    return st_filestat.st_size;
}

// 重置文件的修改时间属性。
// filename：待重置的文件名，建议采用绝对路径的文件名。
// stime：字符串表示的时间，格式不限，但一定要包括yyyymmddhh24miss，一个都不能少。
// 返回值：true-成功；false-失败，失败的原因保存在errno中。
bool UTime(const char *filename, const char *mtime) {
    struct utimbuf stutimbuf;

    stutimbuf.actime = stutimbuf.modtime = strtotime(mtime);

    if (utime(filename, &stutimbuf) != 0)
        return false;

    return true;
}

// 把字符串表示的时间转换为整数表示的时间。
// stime：字符串表示的时间，格式不限，但一定要包括yyyymmddhh24miss，一个都不能少。
// 返回值：整数表示的时间，如果stime的格式不正确，返回-1。
time_t strtotime(const char *stime) {
    char strtime[21], yyyy[5], mm[3], dd[3], hh[3], mi[3], ss[3];
    memset(strtime, 0, sizeof(strtime));
    memset(yyyy, 0, sizeof(yyyy));
    memset(mm, 0, sizeof(mm));
    memset(dd, 0, sizeof(dd));
    memset(hh, 0, sizeof(hh));
    memset(mi, 0, sizeof(mi));
    memset(ss, 0, sizeof(ss));

    PickNumber(stime, strtime, false, false);

    if (strlen(strtime) != 14)
        return -1;

    strncpy(yyyy, strtime, 4);
    strncpy(mm, strtime + 4, 2);
    strncpy(dd, strtime + 6, 2);
    strncpy(hh, strtime + 8, 2);
    strncpy(mi, strtime + 10, 2);
    strncpy(ss, strtime + 12, 2);

    struct tm time_str;

    time_str.tm_year  = atoi(yyyy) - 1900;
    time_str.tm_mon   = atoi(mm) - 1;
    time_str.tm_mday  = atoi(dd);
    time_str.tm_hour  = atoi(hh);
    time_str.tm_min   = atoi(mi);
    time_str.tm_sec   = atoi(ss);
    time_str.tm_isdst = 0;

    return mktime(&time_str);
}

// 把字符串表示的时间加上一个偏移的秒数后得到一个新的字符串表示的时间。
// in_stime：输入的字符串格式的时间。
// out_stime：输出的字符串格式的时间。
// timetvl：需要偏移的秒数，正数往后偏移，负数往前偏移。
// fmt：输出字符串时间out_stime的格式，与LocalTime函数的fmt参数相同。
// 注意：in_stime和out_stime参数可以是同一个变量的地址，如果调用失败，out_stime的内容会清空。
// 返回值：true-成功，false-失败，如果返回失败，可以认为是in_stime的格式不正确。
bool AddTime(const char *in_stime, char *out_stime, const int timetvl, const char *fmt) {
    if ((in_stime == 0) || (out_stime == 0))
        return false;

    time_t timer;
    if ((timer = strtotime(in_stime)) == -1) {
        strcpy(out_stime, "");
        return false;
    }

    timer = timer + timetvl;

    strcpy(out_stime, "");

    timetostr(timer, out_stime, fmt);

    return true;
}

// 获取文件的时间。
// filename：待获取的文件名，建议采用绝对路径的文件名。
// mtime：用于存放文件的时间，即stat结构体的st_mtime。
// fmt：设置时间的输出格式，与LocalTime函数相同，但缺省是"yyyymmddhh24miss"。
// 返回值：如果文件不存在或没有访问权限，返回false，成功返回true。
bool FileMTime(const char *filename, char *mtime, const char *fmt) {
    // 判断文件是否存在。
    struct stat st_filestat;

    if (stat(filename, &st_filestat) < 0)
        return false;

    char strfmt[25];
    memset(strfmt, 0, sizeof(strfmt));
    if (fmt == 0)
        strcpy(strfmt, "yyyymmddhh24miss");
    else
        strcpy(strfmt, fmt);

    timetostr(st_filestat.st_mtime, mtime, strfmt);

    return true;
}

CDir::CDir() {
    m_pos = 0;

    memset(m_DateFMT, 0, sizeof(m_DateFMT));
    strcpy(m_DateFMT, "yyyy-mm-dd hh24:mi:ss");

    m_vFileName.clear();

    initdata();
}

void CDir::initdata() {
    memset(m_DirName, 0, sizeof(m_DirName));
    memset(m_FileName, 0, sizeof(m_FileName));
    memset(m_FullFileName, 0, sizeof(m_FullFileName));
    m_FileSize = 0;
    memset(m_CreateTime, 0, sizeof(m_CreateTime));
    memset(m_ModifyTime, 0, sizeof(m_ModifyTime));
    memset(m_AccessTime, 0, sizeof(m_AccessTime));
}

// 设置文件时间的格式，支持"yyyy-mm-dd hh24:mi:ss"和"yyyymmddhh24miss"两种，缺省是前者。
void CDir::SetDateFMT(const char *in_DateFMT) {
    memset(m_DateFMT, 0, sizeof(m_DateFMT));
    strcpy(m_DateFMT, in_DateFMT);
}

// 打开目录，获取目录中的文件列表信息，存放于m_vFileName容器中。
// in_DirName，待打开的目录名。
// in_MatchStr，待获取文件名的匹配规则，不匹配的文件被忽略。
// in_MaxCount，获取文件的最大数量，缺省值为10000个。
// bAndChild，是否打开各级子目录，缺省值为false-不打开子目录。
// bSort，是否对获取到的文件列表（即m_vFileName容器中的内容）进行排序，缺省值为false-不排序。
// 返回值：如果in_DirName参数指定的目录不存在，OpenDir方法会创建该目录，如果创建失败，返回false，还有，如果当前用户对in_DirName目录下的子目录没有读取权限也会返回false，其它正常情况下都会返回true。
bool CDir::OpenDir(const char *in_DirName, const char *in_MatchStr, const unsigned int in_MaxCount, const bool bAndChild, bool bSort) {
    m_pos = 0;
    m_vFileName.clear();

    // 如果目录不存在，就创建该目录
    if (MKDIR(in_DirName, false) == false)
        return false;

    bool bRet = _OpenDir(in_DirName, in_MatchStr, in_MaxCount, bAndChild);

    if (bSort == true) {
        sort(m_vFileName.begin(), m_vFileName.end());
    }

    return bRet;
}

// 这是一个递归函数，用于OpenDir()的调用，在CDir类的外部不需要调用它。
bool CDir::_OpenDir(const char *in_DirName, const char *in_MatchStr, const unsigned int in_MaxCount, const bool bAndChild) {
    DIR *dir;

    if ((dir = opendir(in_DirName)) == 0)
        return false;

    char strTempFileName[1024];

    struct dirent *st_fileinfo;
    struct stat    st_filestat;

    while ((st_fileinfo = readdir(dir)) != 0) {
        // 以"."打头的文件不处理
        if (st_fileinfo->d_name[0] == '.')
            continue;

        memset(strTempFileName, 0, sizeof(strTempFileName));

        snprintf(strTempFileName, 300, "%s//%s", in_DirName, st_fileinfo->d_name);

        UpdateStr(strTempFileName, "//", "/");

        stat(strTempFileName, &st_filestat);

        // 判断是否是目录
        if (S_ISDIR(st_filestat.st_mode)) {
            if (bAndChild == true) {
                if (_OpenDir(strTempFileName, in_MatchStr, in_MaxCount, bAndChild) == false) {
                    closedir(dir);
                    return false;
                }
            }
        }
        else {
            if (MatchFileName(st_fileinfo->d_name, in_MatchStr) == false)
                continue;

            m_vFileName.push_back(strTempFileName);

            if (m_vFileName.size() >= in_MaxCount)
                break;
        }
    }

    closedir(dir);

    return true;
}

/*
st_gid
  Numeric identifier of group that owns file (UNIX-specific) This field will always be zero on NT systems. A redirected file is classified as an NT file.
st_atime
  Time of last access of file.
st_ctime
  Time of creation of file.
st_dev
  Drive number of the disk containing the file (same as st_rdev).
st_ino
  Number of the information node (the inode) for the file (UNIX-specific). On UNIX file systems, the inode describes the file date and time stamps, permissions, and content. When files are hard-linked to one another, they share the same inode. The inode, and therefore st_ino, has no meaning in the FAT, HPFS, or NTFS file systems.
st_mode
  Bit mask for file-mode information. The _S_IFDIR bit is set if path specifies a directory; the _S_IFREG bit is set if path specifies an ordinary file or a device. User read/write bits are set according to the file’s permission mode; user execute bits are set according to the filename extension.
st_mtime
  Time of last modification of file.
st_nlink
  Always 1 on non-NTFS file systems.
st_rdev
  Drive number of the disk containing the file (same as st_dev).
st_size
  Size of the file in bytes; a 64-bit integer for _stati64 and _wstati64
st_uid
  Numeric identifier of user who owns file (UNIX-specific). This field will always be zero on NT systems. A redirected file is classified as an NT file.
*/

// 从m_vFileName容器中获取一条记录（文件名），同时得到该文件的大小、修改时间等信息。
// 调用OpenDir方法时，m_vFileName容器被清空，m_pos归零，每调用一次ReadDir方法m_pos加1。
// 当m_pos小于m_vFileName.size()，返回true，否则返回false。
bool CDir::ReadDir() {
    initdata();

    int ivsize = m_vFileName.size();

    // 如果已读完，清空容器
    if (m_pos >= ivsize) {
        m_pos = 0;
        m_vFileName.clear();
        return false;
    }

    int pos = 0;

    pos = m_vFileName[m_pos].find_last_of("/");

    // 目录名
    memset(m_DirName, 0, sizeof(m_DirName));
    strcpy(m_DirName, m_vFileName[m_pos].substr(0, pos).c_str());

    // 文件名
    memset(m_FileName, 0, sizeof(m_FileName));
    strcpy(m_FileName, m_vFileName[m_pos].substr(pos + 1, m_vFileName[m_pos].size() - pos - 1).c_str());

    // 文件全名，包括路径
    snprintf(m_FullFileName, 300, "%s", m_vFileName[m_pos].c_str());

    struct stat st_filestat;

    stat(m_FullFileName, &st_filestat);

    m_FileSize = st_filestat.st_size;

    struct tm nowtimer;

    if (strcmp(m_DateFMT, "yyyy-mm-dd hh24:mi:ss") == 0) {
        nowtimer = *localtime(&st_filestat.st_mtime);
        nowtimer.tm_mon++;
        snprintf(m_ModifyTime, 20, "%04u-%02u-%02u %02u:%02u:%02u",
                 nowtimer.tm_year + 1900, nowtimer.tm_mon, nowtimer.tm_mday,
                 nowtimer.tm_hour, nowtimer.tm_min, nowtimer.tm_sec);

        nowtimer = *localtime(&st_filestat.st_ctime);
        nowtimer.tm_mon++;
        snprintf(m_CreateTime, 20, "%04u-%02u-%02u %02u:%02u:%02u",
                 nowtimer.tm_year + 1900, nowtimer.tm_mon, nowtimer.tm_mday,
                 nowtimer.tm_hour, nowtimer.tm_min, nowtimer.tm_sec);

        nowtimer = *localtime(&st_filestat.st_atime);
        nowtimer.tm_mon++;
        snprintf(m_AccessTime, 20, "%04u-%02u-%02u %02u:%02u:%02u",
                 nowtimer.tm_year + 1900, nowtimer.tm_mon, nowtimer.tm_mday,
                 nowtimer.tm_hour, nowtimer.tm_min, nowtimer.tm_sec);
    }

    if (strcmp(m_DateFMT, "yyyymmddhh24miss") == 0) {
        nowtimer = *localtime(&st_filestat.st_mtime);
        nowtimer.tm_mon++;
        snprintf(m_ModifyTime, 20, "%04u%02u%02u%02u%02u%02u",
                 nowtimer.tm_year + 1900, nowtimer.tm_mon, nowtimer.tm_mday,
                 nowtimer.tm_hour, nowtimer.tm_min, nowtimer.tm_sec);

        nowtimer = *localtime(&st_filestat.st_ctime);
        nowtimer.tm_mon++;
        snprintf(m_CreateTime, 20, "%04u%02u%02u%02u%02u%02u",
                 nowtimer.tm_year + 1900, nowtimer.tm_mon, nowtimer.tm_mday,
                 nowtimer.tm_hour, nowtimer.tm_min, nowtimer.tm_sec);

        nowtimer = *localtime(&st_filestat.st_atime);
        nowtimer.tm_mon++;
        snprintf(m_AccessTime, 20, "%04u%02u%02u%02u%02u%02u",
                 nowtimer.tm_year + 1900, nowtimer.tm_mon, nowtimer.tm_mday,
                 nowtimer.tm_hour, nowtimer.tm_min, nowtimer.tm_sec);
    }

    m_pos++;

    return true;
}

CDir::~CDir() {
    m_vFileName.clear();

    // m_vDirName.clear();
}

// 删除目录中的文件，类似Linux系统的rm命令。
// filename：待删除的文件名，建议采用绝对路径的文件名，例如/tmp/root/data.xml。
// times：执行删除文件的次数，缺省是1，建议不要超过3，从实际应用的经验看来，如果删除文件第1次不成功，再尝试
// 2次是可以的，更多就意义不大了。还有，如果执行删除失败，usleep(100000)后再重试。
// 返回值：true-删除成功；false-删除失败，失败的主要原因是权限不足。
// 在应用开发中，可以用REMOVE函数代替remove库函数。
bool REMOVE(const char *filename, const int times) {
    // 如果文件不存在，直接返回失败
    if (access(filename, R_OK) != 0)
        return false;

    for (int ii = 0; ii < times; ii++) {
        if (remove(filename) == 0)
            return true;

        usleep(100000);
    }

    return false;
}

// 把文件重命名，类似Linux系统的mv命令。
// srcfilename：原文件名，建议采用绝对路径的文件名。
// destfilename：目标文件名，建议采用绝对路径的文件名。
// times：执行重命名文件的次数，缺省是1，建议不要超过3，从实际应用的经验看来，如果重命名文件第1次不成功，再尝
// 试2次是可以的，更多次就意义不大了。还有，如果执行重命名失败，usleep(100000)后再重试。
// 返回值：true-重命名成功；false-重命名失败，失败的主要原因是权限不足或磁盘空间不够，如果原文件和目标文件不
// 在同一个磁盘分区，重命名也可能失败。
// 注意，在重命名文件之前，会自动创建destfilename参数中的目录名。
// 在应用开发中，可以用RENAME函数代替rename库函数。
bool RENAME(const char *srcfilename, const char *dstfilename, const int times) {
    // 如果文件不存在，直接返回失败
    if (access(srcfilename, R_OK) != 0)
        return false;

    if (MKDIR(dstfilename) == false)
        return false;

    for (int ii = 0; ii < times; ii++) {
        if (rename(srcfilename, dstfilename) == 0)
            return true;

        usleep(100000);
    }

    return false;
}

CTcpClient::CTcpClient() {
    m_sockfd = -1;
    memset(m_ip, 0, sizeof(m_ip));
    m_port     = 0;
    m_btimeout = false;
}

bool CTcpClient::ConnectToServer(const char *ip, const int port) {
    if (m_sockfd != -1) {
        close(m_sockfd);
        m_sockfd = -1;
    }

    strcpy(m_ip, ip);
    m_port = port;

    struct hostent    *h;
    struct sockaddr_in servaddr;

    if ((m_sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        return false;

    if (!(h = gethostbyname(m_ip))) {
        close(m_sockfd);
        m_sockfd = -1;
        return false;
    }

    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port   = htons(m_port);  // 指定服务端的通讯端口
    memcpy(&servaddr.sin_addr, h->h_addr, h->h_length);

    if (connect(m_sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) != 0) {
        close(m_sockfd);
        m_sockfd = -1;
        return false;
    }

    return true;
}

bool CTcpClient::Read(char *buffer, const int itimeout) {
    if (m_sockfd == -1)
        return false;

    if (itimeout > 0) {
        fd_set tmpfd;

        FD_ZERO(&tmpfd);
        FD_SET(m_sockfd, &tmpfd);

        struct timeval timeout;
        timeout.tv_sec  = itimeout;
        timeout.tv_usec = 0;

        m_btimeout = false;

        int i;
        if ((i = select(m_sockfd + 1, &tmpfd, 0, 0, &timeout)) <= 0) {
            if (i == 0)
                m_btimeout = true;
            return false;
        }
    }

    m_buflen = TcpRead(m_sockfd, buffer, sizeof(buffer), 0);
    if (m_buflen < 0)
        return false;
    else
        return true;

    // m_buflen = 0;
    // return (TcpRead(m_sockfd, buffer, &m_buflen));
}

bool CTcpClient::Write(const char *buffer, const int ibuflen) {
    if (m_sockfd == -1)
        return false;

    fd_set tmpfd;

    FD_ZERO(&tmpfd);
    FD_SET(m_sockfd, &tmpfd);

    struct timeval timeout;
    timeout.tv_sec  = 5;
    timeout.tv_usec = 0;

    m_btimeout = false;

    int i;
    if ((i = select(m_sockfd + 1, 0, &tmpfd, 0, &timeout)) <= 0) {
        if (i == 0)
            m_btimeout = true;
        return false;
    }

    int ilen = ibuflen;

    if (ibuflen == 0)
        ilen = strlen(buffer);

    return (TcpWrite(m_sockfd, buffer, ilen));
}

void CTcpClient::Close() {
    if (m_sockfd > 0)
        close(m_sockfd);

    m_sockfd = -1;
    memset(m_ip, 0, sizeof(m_ip));
    m_port     = 0;
    m_btimeout = false;
}

CTcpClient::~CTcpClient() {
    Close();
}

CTcpServer::CTcpServer() {
    m_listenfd = -1;
    m_connfd   = -1;
    m_socklen  = 0;
    m_btimeout = false;
}

CTcpServer::CTcpServer(CTcpServer &server) {
    this->m_connfd     = server.m_connfd;
    this->m_socklen    = server.m_socklen;
    this->m_clientaddr = server.m_clientaddr;
    this->m_servaddr   = server.m_servaddr;
}

bool CTcpServer::InitServer(const unsigned int port) {
    if (m_listenfd > 0) {
        close(m_listenfd);
        m_listenfd = -1;
    }

    m_listenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (m_listenfd < 0) {
        perror("socket");
    }

    int          opt = 1;
    unsigned int len = sizeof(opt);
    int          ret = setsockopt(m_listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, len);
    if (ret < 0) {
        perror("setsockopt");
    }
    //将套接字和IP、端口绑定
    memset(&m_servaddr, 0, sizeof(m_servaddr));  //每个字节都用0填充
    m_servaddr.sin_family      = AF_INET;        //使用IPv4地址
    m_servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    m_servaddr.sin_port        = htons(port);  //端口
    ret                        = bind(m_listenfd, (struct sockaddr *)&m_servaddr, sizeof(m_servaddr));
    if (ret < 0) {
        perror("bind");
    }
    //进入监听状态，等待用户发起请求
    if (listen(m_listenfd, 5) != 0) {
        perror("listen");
        CloseListen();
        return false;
    }

    m_socklen = sizeof(struct sockaddr_in);

    return true;
}

bool CTcpServer::Accept(int itimeout) {
    if (m_listenfd == -1)
        return false;
    if (itimeout > 0) {
        fd_set tmpfd;

        FD_ZERO(&tmpfd);
        FD_SET(m_listenfd, &tmpfd);

        struct timeval timeout;
        timeout.tv_sec  = itimeout;
        timeout.tv_usec = 0;

        m_btimeout = false;

        int i;
        if ((i = select(m_listenfd + 1, &tmpfd, 0, 0, &timeout)) <= 0) {
            if (i == 0)
                m_btimeout = true;
            return false;
        }
    }

    if ((m_connfd = accept(m_listenfd, (struct sockaddr *)&m_clientaddr, (socklen_t *)&m_socklen)) < 0)
        return false;

    return true;
}

char *CTcpServer::GetIP() {
    return (inet_ntoa(m_clientaddr.sin_addr));
}

bool CTcpServer::Read(char *buffer, size_t buffer_size, const int itimeout) {
    if (m_connfd == -1)
        return false;

    if (itimeout > 0) {
        fd_set tmpfd;

        FD_ZERO(&tmpfd);
        FD_SET(m_connfd, &tmpfd);

        struct timeval timeout;
        timeout.tv_sec  = itimeout;
        timeout.tv_usec = 0;

        m_btimeout = false;

        int i;
        if ((i = select(m_connfd + 1, &tmpfd, 0, 0, &timeout)) <= 0) {
            if (i == 0)
                m_btimeout = true;
            return false;
        }
    }

    m_buflen = recv(m_connfd, buffer, buffer_size, 0);

    return m_buflen <= 0 ? false : true;
}

bool CTcpServer::Write(const char *buffer, const int ibuflen) {
    if (m_connfd == -1)
        return false;

    fd_set tmpfd;

    FD_ZERO(&tmpfd);
    FD_SET(m_connfd, &tmpfd);

    struct timeval timeout;
    timeout.tv_sec  = 5;
    timeout.tv_usec = 0;

    m_btimeout = false;

    int i;
    if ((i = select(m_connfd + 1, 0, &tmpfd, 0, &timeout)) <= 0) {
        if (i == 0)
            m_btimeout = true;
        return false;
    }

    int ilen = ibuflen;
    if (ilen == 0)
        ilen = strlen(buffer);
    return Writen(m_connfd, buffer, ibuflen);

    // return (TcpWrite(m_connfd, buffer, ilen));
}

void CTcpServer::CloseListen() {
    if (m_listenfd > 0) {
        close(m_listenfd);
        m_listenfd = -1;
    }
}

void CTcpServer::CloseClient() {
    if (m_connfd > 0) {
        close(m_connfd);
        m_connfd = -1;
    }
}

CTcpServer::~CTcpServer() {
    CloseListen();
    CloseClient();
}

bool TcpRead(const int sockfd, char *buffer, int *ibuflen, const int itimeout) {
    if (sockfd == -1)
        return false;

    if (itimeout > 0) {
        fd_set tmpfd;

        FD_ZERO(&tmpfd);
        FD_SET(sockfd, &tmpfd);

        struct timeval timeout;
        timeout.tv_sec  = itimeout;
        timeout.tv_usec = 0;

        int i;
        if ((i = select(sockfd + 1, &tmpfd, 0, 0, &timeout)) <= 0)
            return false;
    }

    (*ibuflen) = 0;

    if (Readn(sockfd, (char *)ibuflen, 4) == false)
        return false;

    (*ibuflen) = ntohl(*ibuflen);  // 把网络字节序转换为主机字节序。

    if (Readn(sockfd, buffer, (*ibuflen)) == false)
        return false;

    return true;
}

size_t TcpRead(const int sockfd, char *buffer, size_t buff_size, const int itimeout) {
    if (sockfd == -1)
        return false;

    if (itimeout > 0) {
        fd_set tmpfd;

        FD_ZERO(&tmpfd);
        FD_SET(sockfd, &tmpfd);

        struct timeval timeout;
        timeout.tv_sec  = itimeout;
        timeout.tv_usec = 0;

        int i;
        if ((i = select(sockfd + 1, &tmpfd, 0, 0, &timeout)) <= 0)
            return false;
    }

    size_t ibuflen = 0;
    return recv(sockfd, buffer, buff_size, 0);
}

bool TcpWrite(const int sockfd, const char *buffer, const int ibuflen) {
    if (sockfd == -1)
        return false;

    fd_set tmpfd;

    FD_ZERO(&tmpfd);
    FD_SET(sockfd, &tmpfd);

    struct timeval timeout;
    timeout.tv_sec  = 5;
    timeout.tv_usec = 0;

    if (select(sockfd + 1, 0, &tmpfd, 0, &timeout) <= 0)
        return false;

    int ilen = 0;

    // 如果长度为0，就采用字符串的长度
    if (ibuflen == 0)
        ilen = strlen(buffer);
    else
        ilen = ibuflen;

    int ilenn = htonl(ilen);  // 转换为网络字节序。

    char strTBuffer[ilen + 4];
    memset(strTBuffer, 0, sizeof(strTBuffer));
    memcpy(strTBuffer, &ilenn, 4);
    memcpy(strTBuffer + 4, buffer, ilen);

    return Writen(sockfd, strTBuffer, ilen + 4);
}

bool Readn(const int sockfd, char *buffer, const size_t n) {
    int nLeft, nread, idx;

    nLeft = n;
    idx   = 0;

    while (nLeft > 0) {
        if ((nread = recv(sockfd, buffer + idx, nLeft, 0)) <= 0)
            return false;

        idx += nread;
        nLeft -= nread;
    }

    return true;
}

bool Writen(const int sockfd, const char *buffer, const size_t n) {
    int nLeft, idx, nwritten;
    nLeft = n;
    idx   = 0;
    while (nLeft > 0) {
        if ((nwritten = send(sockfd, buffer + idx, nLeft, MSG_NOSIGNAL)) <= 0){
            perror("send");
            return false;
        }

        nLeft -= nwritten;
        idx += nwritten;
    }

    return true;
}

// 把文件通过sockfd发送给对端
bool SendFile(int sockfd, struct st_fileinfo *stfileinfo, CLogFile *logfile) {
    char strSendBuffer[301], strRecvBuffer[301];
    memset(strSendBuffer, 0, sizeof(strSendBuffer));

    snprintf(strSendBuffer, 300, "<filename>%s</filename><filesize>%d</filesize><mtime>%s</mtime>", stfileinfo->filename, stfileinfo->filesize, stfileinfo->mtime);

    if (TcpWrite(sockfd, strSendBuffer) == false) {
        if (logfile != 0)
            logfile->Write("SendFile TcpWrite() failed.\n");
        return false;
    }

    int  bytes       = 0;
    int  total_bytes = 0;
    int  onread      = 0;
    char buffer[1000];

    FILE *fp = 0;

    if ((fp = FOPEN(stfileinfo->filename, "rb")) == 0) {
        if (logfile != 0)
            logfile->Write("SendFile FOPEN(%s) failed.\n", stfileinfo->filename);
        return false;
    }

    while (true) {
        memset(buffer, 0, sizeof(buffer));

        if ((stfileinfo->filesize - total_bytes) > 1000)
            onread = 1000;
        else
            onread = stfileinfo->filesize - total_bytes;

        bytes = fread(buffer, 1, onread, fp);

        if (bytes > 0) {
            if (Writen(sockfd, buffer, bytes) == false) {
                if (logfile != 0)
                    logfile->Write("SendFile Writen() failed.\n");
                fclose(fp);
                fp = 0;
                return false;
            }
        }

        total_bytes = total_bytes + bytes;

        if ((int)total_bytes == stfileinfo->filesize)
            break;
    }

    fclose(fp);

    // 接收对端返回的确认报文
    int buflen = 0;
    memset(strRecvBuffer, 0, sizeof(strRecvBuffer));
    if (TcpRead(sockfd, strRecvBuffer, &buflen) == false) {
        if (logfile != 0)
            logfile->Write("SendFile TcpRead() failed.\n");
        return false;
    }

    if (strcmp(strRecvBuffer, "ok") != 0)
        return false;

    return true;
}

// 接收通过socdfd发送过来的文件
bool RecvFile(int sockfd, struct st_fileinfo *stfileinfo, CLogFile *logfile) {
    char strSendBuffer[301];

    char strfilenametmp[301];
    memset(strfilenametmp, 0, sizeof(strfilenametmp));
    sprintf(strfilenametmp, "%s.tmp", stfileinfo->filename);

    FILE *fp = 0;

    if ((fp = FOPEN(strfilenametmp, "wb")) == 0)  // FOPEN可创建目录
    {
        if (logfile != 0)
            logfile->Write("RecvFile FOPEN %s failed.\n", strfilenametmp);
        return false;
    }

    int  total_bytes = 0;
    int  onread      = 0;
    char buffer[1000];

    while (true) {
        memset(buffer, 0, sizeof(buffer));

        if ((stfileinfo->filesize - total_bytes) > 1000)
            onread = 1000;
        else
            onread = stfileinfo->filesize - total_bytes;

        if (Readn(sockfd, buffer, onread) == false) {
            if (logfile != 0)
                logfile->Write("RecvFile Readn() failed.\n");
            fclose(fp);
            fp = 0;
            return false;
        }

        fwrite(buffer, 1, onread, fp);

        total_bytes = total_bytes + onread;

        if ((int)total_bytes == stfileinfo->filesize)
            break;
    }

    fclose(fp);

    // 重置文件的时间
    UTime(strfilenametmp, stfileinfo->mtime);

    memset(strSendBuffer, 0, sizeof(strSendBuffer));
    if (RENAME(strfilenametmp, stfileinfo->filename) == true)
        strcpy(strSendBuffer, "ok");
    else
        strcpy(strSendBuffer, "failed");

    // 向对端返回响应内容
    if (TcpWrite(sockfd, strSendBuffer) == false) {
        if (logfile != 0)
            logfile->Write("RecvFile TcpWrite() failed.\n");
        return false;
    }

    if (strcmp(strSendBuffer, "ok") != 0)
        return false;

    return true;
}

// 复制文件，类似Linux系统的cp命令。
// srcfilename：原文件名，建议采用绝对路径的文件名。
// destfilename：目标文件名，建议采用绝对路径的文件名。
// 返回值：true-复制成功；false-复制失败，失败的主要原因是权限不足或磁盘空间不够。
// 注意：
// 1）在复制名文件之前，会自动创建destfilename参数中的目录名。
// 2）复制文件的过程中，采用临时文件命名的方法，复制完成后再改名为destfilename，避免中间状态的文件被读取。
// 3）复制后的文件的时间与原文件相同，这一点与Linux系统cp命令不同。
bool COPY(const char *srcfilename, const char *dstfilename) {
    if (MKDIR(dstfilename) == false)
        return false;

    char strdstfilenametmp[301];
    memset(strdstfilenametmp, 0, sizeof(strdstfilenametmp));
    snprintf(strdstfilenametmp, 300, "%s.tmp", dstfilename);

    int srcfd, dstfd;

    srcfd = dstfd = -1;

    int iFileSize = FileSize(srcfilename);

    int  bytes       = 0;
    int  total_bytes = 0;
    int  onread      = 0;
    char buffer[5000];

    if ((srcfd = open(srcfilename, O_RDONLY)) < 0)
        return false;

    if ((dstfd = open(strdstfilenametmp, O_WRONLY | O_CREAT | O_TRUNC, S_IWUSR | S_IRUSR | S_IXUSR)) < 0) {
        close(srcfd);
        return false;
    }

    while (true) {
        memset(buffer, 0, sizeof(buffer));

        if ((iFileSize - total_bytes) > 5000)
            onread = 5000;
        else
            onread = iFileSize - total_bytes;

        bytes = read(srcfd, buffer, onread);

        if (bytes > 0)
            write(dstfd, buffer, bytes);

        total_bytes = total_bytes + bytes;

        if (total_bytes == iFileSize)
            break;
    }

    close(srcfd);

    close(dstfd);

    // 更改文件的修改时间属性
    char strmtime[21];
    memset(strmtime, 0, sizeof(strmtime));
    FileMTime(srcfilename, strmtime);
    UTime(strdstfilenametmp, strmtime);

    if (RENAME(strdstfilenametmp, dstfilename) == false) {
        REMOVE(strdstfilenametmp);
        return false;
    }

    return true;
}

CTimer::CTimer() {
    memset(&m_start, 0, sizeof(struct timeval));
    memset(&m_end, 0, sizeof(struct timeval));

    // 开始计时
    Start();
}

// 开始计时
void CTimer::Start() {
    gettimeofday(&m_start, 0);
}

// 计算已逝去的时间，单位：秒，小数点后面是微秒
double CTimer::Elapsed() {
    gettimeofday(&m_end, 0);

    double dstart, dend;

    dstart = dend = 0;

    char strtemp[51];
    memset(strtemp, 0, sizeof(strtemp));
    snprintf(strtemp, 30, "%ld.%ld", m_start.tv_sec, m_start.tv_usec);
    dstart = atof(strtemp);

    memset(strtemp, 0, sizeof(strtemp));
    snprintf(strtemp, 30, "%ld.%ld", m_end.tv_sec, m_end.tv_usec);
    dend = atof(strtemp);

    // 重新开始计时
    Start();

    return dend - dstart;
}
